#include "cgeServerSocket.h"
#define CGE_WIN32 666

#include <sys/socket.h>
#include <sys/un.h>
#include <sys/errno.h>

namespace basterd
{
	namespace net
	{
	    // ----------------------------------------------------------------------------
		// ServerSocket
		// ----------------------------------------------------------------------------
        ServerSocket::ServerSocket(int maxAllowedConns)
            : hostAddress("127.0.0.1"), serverAddress(), serverSocketId(0),
			  maxAllowedConnections(maxAllowedConns),port(0), connected(false)
        {

            create();
        }
        // ----------------------------------------------------------------------------
        ServerSocket::ServerSocket(unsigned short porta, int maxConn)
            : hostAddress("127.0.0.1"), serverAddress(), serverSocketId(0),
			  maxAllowedConnections(maxConn), port(porta), connected(false)
        {
			//Engine::getInstance();

            create();
            bind(port);
            listen();
        }
        // ----------------------------------------------------------------------------
        ServerSocket::ServerSocket(const String& hosta, unsigned short porta, int maxConn )
            : hostAddress(hosta), serverAddress(), serverSocketId(0),
			maxAllowedConnections(maxConn), port(porta), connected(false)
        {
			//Engine::getInstance();

            create();
            bind(hostAddress, port);
            listen();
        }
        // ----------------------------------------------------------------------------
        ServerSocket::~ServerSocket(void)
        {
            close();
        }
        // ----------------------------------------------------------------------------
        bool ServerSocket::isConnected(void) const
        {
            return connected;
        }
		// ----------------------------------------------------------------------------
        bool ServerSocket::create(void)
        {
        #if CGE_PLATFORM == CGE_WIN32
            // ----------------------------------------------------
			// windows implementation
			memset( &serverAddress, 0, sizeof(serverAddress) );
            serverSocketId = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP );

            if ( serverSocketId == INVALID_SOCKET )
			{
				CGE_ERRF("Cannot create the server socket");
                return false;
            }
            
            return true;
            
        #elif CGE_PLATFORM == CGE_LINUX || CGE_PLATFORM == CGE_LIN64 || CGE_PLATFORM == CGE_APPLE
            // ----------------------------------------------------
            // linux implementation
            // ----------------------------------------------------
            memset( &serverAddress, 0, sizeof(serverAddress) );
            serverSocketId = ::socket( AF_INET, SOCK_STREAM, 0 );

            if (serverSocketId == -1)
            {
                CGE_ERRF("Cannot create the server socket");
                return false;
            }

            int on = 1;
            socklen_t optlen = sizeof(int);

            return ( !( setsockopt(serverSocketId, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, optlen ) == -1) );
            
        #endif
        }
        // ----------------------------------------------------------------------------
        Socket* ServerSocket::accept(void) const
        {
        #if CGE_PLATFORM == CGE_WIN32
            // ----------------------------------------------------
			// windows implementation			
			// NOTE: we cannot believe this implementation. 
			//sockaddr addr;
			sockaddr_in clientAddress;
			socklen_t slen = sizeof(clientAddress);
			__socket sid = ::accept(serverSocketId, (sockaddr*) &clientAddress, &slen );

			if( sid != INVALID_SOCKET )
			{
				/*sockaddr_in clientAddress;
				memcpy( &clientAddress,addr.sa_data, sizeof(clientAddress) );
				clientAddress.sin_family = addr.sa_family;*/

				Socket* sock = new Socket( sid, clientAddress );

				return sock;
			}

			CGE_LOGF("Cannot accept client! Error code: " << WSAGetLastError() );

            return NULL;
            
        #elif CGE_PLATFORM == CGE_LINUX
            // ----------------------------------------------------
			// linux implementation
			sockaddr_in clientAddress;
			socklen_t slen = sizeof(clientAddress);
			__socket sid = ::accept( serverSocketId, (sockaddr*) &clientAddress, &slen  );
		

			// checks for errors
			if( sid < 1 )
			{
				return NULL;
			}

			// this looks better: we set the socket up
			Socket* sock = new Socket( sid, clientAddress );
			// TODO: initialize some data about the socket (host address and port)

			return sock;
            
        #endif
        }
        // ----------------------------------------------------------------------------
        void ServerSocket::close(void) const
        {
            if ( connected )
            {
            #if CGE_PLATFORM == CGE_WIN32
                // ----------------------------------------------------
			    // windows implementation
                ::closesocket( serverSocketId );                
                
            #elif CGE_PLATFORM == CGE_LINUX
                // ----------------------------------------------------
    			// linux implementation
                ::close( serverSocketId );
                
            #endif
            }
        }
        // ----------------------------------------------------------------------------
        unsigned short ServerSocket::getPort(void) const
        {
            return port;
        }
        // ----------------------------------------------------------------------------
		const String& ServerSocket::getHostAddress(void) const
		{
			return hostAddress;
		}		
        // ----------------------------------------------------------------------------
        bool ServerSocket::bind(unsigned short _port)
        {
			this->port = _port; 

			char myname[256];
			if( ::gethostname ( myname, sizeof ( myname ) ) == 0 )
			{
				hostAddress = myname;
				CGE_LOGF("Binding port "<< port << " to this server (" << myname << ")" );
			}
           
        #if CGE_PLATFORM == CGE_WIN32
            // ----------------------------------------------------
			// windows implementation
			memset(&serverAddress,0,sizeof(serverAddress));
            serverAddress.sin_family = AF_INET;
            serverAddress.sin_addr.s_addr = INADDR_ANY;
            serverAddress.sin_port = htons(port);

            if ( !( ::bind(serverSocketId, (SOCKADDR*) &serverAddress, sizeof(serverAddress)) == SOCKET_ERROR ) )
			{
				int yes=1;
				//char yes='1'; // NOTE: Solaris people use this

				// lose the pesky "Address already in use" error message
				if (setsockopt(serverSocketId,SOL_SOCKET,SO_REUSEADDR, (char*)&yes,sizeof(int)) == -1) {
					perror("setsockopt");
					exit(1);
				} 

				return true;
			}

			CGE_LOGF("Cannot bind server socket to port " << port );
			return false;
        
        #elif CGE_PLATFORM == CGE_LINUX
            // ----------------------------------------------------
			// linux implementation
			memset(&serverAddress,0,sizeof(serverAddress));
			//serverAddress.sin_family = AF_INET;
			serverAddress.sin_family = AF_INET;
			serverAddress.sin_addr.s_addr = INADDR_ANY;//inet_addr(hostAddress.c_str());
			serverAddress.sin_port = ::htons(port);

			int res = ::bind(serverSocketId, (struct sockaddr*) &serverAddress, sizeof(serverAddress));

			return res != -1;
    
        #endif
        }
        // ----------------------------------------------------------------------------
        bool ServerSocket::bind(const String& host, unsigned short porta)
        {
        	// TODO: STORE LOCAL HOST and PORT
			hostAddress = host;
			this->port = porta;
        	
        #if CGE_PLATFORM == CGE_WIN32
            // ----------------------------------------------------
	    // windows implementation	

            serverAddress.sin_family = AF_INET;
            serverAddress.sin_addr.s_addr = inet_addr(hostAddress.c_str());
            serverAddress.sin_port = htons(port);

            if ( !( ::bind(serverSocketId, (SOCKADDR*) &serverAddress, sizeof(serverAddress)) == SOCKET_ERROR ) )
			{
				return true;
			}

			CGE_LOGF("Cannot bind server socket to " << hostAddress << ":" << port );
			return false;
        
        #elif CGE_PLATFORM == CGE_LINUX
            // ----------------------------------------------------
			// linux implementation
            serverAddress.sin_family = AF_INET;
            serverAddress.sin_addr.s_addr = inet_addr(hostAddress.c_str());
            serverAddress.sin_port = htons(port);

            int code = ::bind(serverSocketId, (struct sockaddr*) &serverAddress, sizeof(serverAddress));
            if ( code == 0	 )
			{
            	CGE_LOGF("server bound to " << port );
				return true;
			}

			CGE_LOGF("Cannot bind server socket to " << hostAddress << ":" << port );
			switch(errno)
			{
			break;
			case EADDRINUSE:
				CGE_LOGF("\nThe given address is already in use."); break;
			case EBADF:
				CGE_LOGF("\nsockfd is not a valid descriptor."); break;

			/*case EINVAL:
				CGE_LOGF("The socket is already bound to an address."); break;*/

			case ENOTSOCK:
				CGE_LOGF("sockfd is a descriptor for a file, not a socket."); break;
			case EACCES:
				CGE_LOGF("Search permission is denied on a component of the path prefix. (See also path_resolution(7).)"); break;

			case EADDRNOTAVAIL:
				CGE_LOGF("A nonexistent interface was requested or the requested address was not local."); break;
			case EFAULT:
				CGE_LOGF("addr points outside the user's accessible address space."); break;

			case EINVAL:
				CGE_LOGF("The addrlen is wrong, or the socket was not in the AF_UNIX family."); break;

			case ELOOP:
				CGE_LOGF("Too many symbolic links were encountered in resolving addr."); break;

			case ENAMETOOLONG:
				CGE_LOGF("addr is too long."); break;
			case ENOENT:
				CGE_LOGF("The file does not exist."); break;

			case ENOMEM:
				CGE_LOGF("Insufficient kernel memory was available."); break;

			case ENOTDIR:
				CGE_LOGF("A component of the path prefix is not a directory."); break;
			case EROFS:
				CGE_LOGF("The socket inode would reside on a read-only file system."); break;
			}
			return false;
            
        #endif
        }
        // ----------------------------------------------------------------------------
        bool ServerSocket::listen(void) const
        {
        #if CGE_PLATFORM == CGE_WIN32
            // ----------------------------------------------------
			// windows implementation
            return ( !(::listen (serverSocketId, maxAllowedConnections) == SOCKET_ERROR) );
            
        #elif CGE_PLATFORM == CGE_LINUX
            // ----------------------------------------------------
			// linux implementation
            return ::listen (serverSocketId, maxAllowedConnections) != -1;
            
        #endif
        }        
        // ----------------------------------------------------------------------------
	}
}
